#pragma once
#include <vector>
#include "DataStructure/Common/Out.h"
#include "DataStructure/Common/SGNamespace.h"
#include "DataStructure/Input/Constraint/DOFTypes.h"
#include "DataStructure/Input/Solver/DOFDirections.h"
#include "DataStructure/Input/Solver/Subcase.h"
#include "DataStructure/Input/Solver/FieldTypes.h"

#include "DBManager/DBServiceFactory.h"
#include "../Common/PreExport.h"
#ifdef TEST
    #include "gtest/gtest_prod.h"
#endif
SG_NAMESPACE_OPEN
namespace FEMSolver
{
/** @brief 自由度处理
 * 
 * 计算各个自由度在全局的编号，服务于后续组装过程
 */
class Pre_DECLSPEC DOFHandler
{
private:
    /**  @brief  标记节点激活自由度
     * @param index     节点对应的index
     * @param dofSet    自由度集合
     * @param DOFFlag   自由度类型集合
     */
    static void MarkNodeActiveDOFs (std::size_t index, const SG::DataStructure::FEM::DofSet& dofSet,  _OUT std::vector<SG::DataStructure::FEM::DOFTypes>& DOFFlag, std::size_t& nFreeDof);

    /** @brief 将节点的某个自由度标记为激活
     * 
    */
    static void MarkNodeActiveDOFs (std::size_t index, SG::DataStructure::FEM::DOFDirections dof,  _OUT std::vector<SG::DataStructure::FEM::DOFTypes>& DOFFlag, std::size_t& nFreeDof);

    /** @brief 遍历约束及多点约束方程，修改 DOFFlag相应位置的值
     */
    void MarkSPCSlaveDOFs (SG::DataStructure::Common::Id nodeId, const SG::DataStructure::FEM::DofSet& dofSet,  _OUT std::vector<SG::DataStructure::FEM::DOFTypes>& DOFFlag);

    /** @brief 将节点指定自由度标记为 DOFTYPE_Constraint 
     * 
     * 修改 对应的 自由自由度集合和 m-set 集合数量
     * @param nodeId 
     * @param dofSet 
     * @param DOFFlag
     * @param checkRepet 对于 CWELD CFAST放松限制 
     */
    void MarkMPCDependentDOFs (std::size_t nodeId, const SG::DataStructure::FEM::DofSet& dofSet,  _OUT std::vector<SG::DataStructure::FEM::DOFTypes>& DOFFlag, bool checkRepet = false);

    static void ActiveThermalFieldDOF (const SG::DBManager::DBServiceFactorySP dbServiceFactory, _OUT std::size_t&  nFreeDofs,  _OUT std::vector<SG::DataStructure::FEM::DOFTypes>& DOFFlag);
    
    /**
     * @brief   : 激活弹性场节点自由度
     * @param   : dbServiceFactory 
     * @param   : nFreeDofs 
     * @param   : DOFFlag 
     * @author  : wanghuan
     * @date    : 2024.10.26
     */
    static void ActiveElasticFieldDOF (const SG::DBManager::DBServiceFactorySP dbServiceFactory, _OUT std::size_t& nFreeDofs,  _OUT std::vector<SG::DataStructure::FEM::DOFTypes>& DOFFlag);

    /** @brief  标记节点自由度对应的类型
     * @param dbServiceFactory 指向网格的指针
     * @param subcase   工况信息
     * @param [out] DOFFlag   节点自由度的类型
    */
    void MarkDOFType (const SG::DBManager::DBServiceFactorySP dbServiceFactory, const SG::DataStructure::FEM::Subcase& subcase, _OUT std::vector<SG::DataStructure::FEM::DOFTypes>& DOFFlag);
    
    /** @brief 节点全局自由度排序
     * 
     * @param DOFFlag 
     * @param [out] dofGlobalIndex 
    */ 
    void Sort (const std::vector<SG::DataStructure::FEM::DOFTypes>& DOFFlag);
#ifdef TEST  
    FRIEND_TEST (DOFHandlerTest, MarkDofTypeOneBeamNoConstrainCase);
    FRIEND_TEST (DOFHandlerTest, OneBeamCantileverCase);
    FRIEND_TEST (DOFHandlerTest, DofSortIncludeSPC);
    FRIEND_TEST (DOFHandlerTest, TwoLinkTrussCase);
    FRIEND_TEST (DOFHandlerTest, ThermalLinkCase);
#endif

public:     
    DOFHandler () = default;

    /** @brief 自由度处理
     * @param nodeIds 所有节点编号
     * @param[in] field     求解域类型，决定了每个节点最大分配节点自由度
     * @param nodeindex node 在数据库中对应索引位置
     */
    DOFHandler (const std::vector<SG::DataStructure::Common::Id>& nodeIds, SG::DataStructure::FEM::Field_Types field = SG::DataStructure::FEM::Field_Types::FIELD_Elastic, const std::vector<std::size_t>& nodeindex = std::vector<std::size_t>());

    /**  @brief 计算自由度全局索引
     * 
     * 按照自由度上约束类型对节点所有的自由度进行排序
     * 依次调用 MarkDOFType 和 Sort函数用于自由度排序
     * @param[in] dbServiceFactory 数据管理对象， 提供网格信息
     * @param[in] subcase   提供工况信息，指定了使用的mpc和spc集编号
    */
    void Compute (const SG::DBManager::DBServiceFactorySP dbServiceFactory, const SG::DataStructure::FEM::Subcase& subcase);

    /**  @brief 通过Id 查询节点的索引 */ 
    static int FindNodeIndex (SG::DataStructure::Common::Id nodeId);

    /** @brief 通过节点index, 查询节点对应自由度方向上的全局自由度
     * 
    */
    int GetGlobalIndex (const std::size_t nodeIndex, const SG::DataStructure::FEM::DOFDirections& dof) const;
  
    /**  @brief 通过一组节点的index以及对应的激活自由度， 查询自由度对应的全局索引编号
     * 
     * 节点有多少个自由度和单元类型有关。 例如：某节点被梁和实体单元共用。作为实体单元节点，这个节点具有3个线自由度；而作为梁节点时，其具有6个自由度
     */
    void GetGlobalIndex (const std::vector<std::size_t>& nodeIndexes, const SG::DataStructure::FEM::DofSet& DOFs, _OUT std::vector<int>& dofGlobalIndex) const;
    
    /// @brief 需要组织的自由度数量， 超过该数值的自由度贡献可以忽略
    std::size_t ComputeSparseMatrixSize () const;

    /// @brief 获取网格中多点约束方程导致的从自由度数量
    std::size_t GetSlaveDOFSize () const;

    /** @brief 网格约束的自由度总数
     * 
     * 网格中激活自由度的总数
     */ 
    std::size_t GetFixedDOFSize () const;

    std::size_t GetNodeSize () const;
    const std::vector<SG::DataStructure::Common::Id>& GetNodeIds () const;

private:
    static std::size_t                             s_maxNodeDOFSize;  ///< 节点最大自由度数量
    static std::vector<SG::DataStructure::Common::Id> s_nodeIds;         ///< 节点id
    static std::vector<std::size_t>                s_nodeindex;       ///< 节点在数据库中储存的索引

    SG::DataStructure::FEM::Field_Types     m_field;           ///< 求解域类型
    std::size_t                             m_nFreeDofs;       ///< 非约束自由度数量
    std::size_t                             m_nFixDofs;        ///< 单点固定约束数量
    std::size_t                             m_nSlaveDofs;      ///< R 单元或者 MPC产生的从自由度数量
    std::vector<int>                        m_dofGlobalIndex;  ///< 节点自由度的全局索引
};

/** @brief 最终计算矩阵的维度
 * 
 * 除开约束和多点约束导致的从自由度等等。
 */
inline std::size_t DOFHandler::ComputeSparseMatrixSize() const
{
    return m_nFreeDofs;
}

/// @brief 获取网格中多点约束方程导致的从自由度数量
inline std::size_t DOFHandler::GetSlaveDOFSize () const
{
    return m_nSlaveDofs;
}

inline std::size_t DOFHandler::GetFixedDOFSize () const
{
    return m_nFixDofs;
}

inline std::size_t DOFHandler::GetNodeSize () const
{
    return s_nodeIds.size();
}

inline const std::vector<SG::DataStructure::Common::Id>& DOFHandler::GetNodeIds () const
{
    return s_nodeIds;
}
}; // end namespace FEMSolver
SG_NAMESPACE_CLOSE